﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using MovieRental.GeneralDataExceptions;

namespace MovieRental.XmlDataProviderCore
{
    public class XmlDataContext : IDisposable
    {
        private XmlDataProvider _provider;
        private XmlEntityConverter _converter = new XmlEntityConverter();
        private bool _disposed = false;
        private HashSet<string> _changedTables = new HashSet<string>();

        internal XmlDataContext(XmlDataProvider provider)
        {
            _provider = provider;
        }

        public IEnumerable<object> GetAllForType(Type type)
        {
            XElement root = _provider.GetRootForEntity(type);
            foreach (XElement item in root.Elements())
            {
                object result;
                try
                {
                     result = _converter.ConvertFromXElement(item, type);
                }
                catch (Exception ex)
                {
                    throw new DbException("Xml conversion error.", ex);
                }
                yield return result;
            }
        }

        public void Save(params object[] objects)
        {
            if (objects == null || objects.Any(x => x == null))
                throw new ArgumentNullException();
            foreach (object obj in objects)
            {
                XElement item = _converter.ConvertToXElement(obj);
                XElement root = _provider.GetRootForEntity(obj);
                string idElementName = _provider.GetIdElementNameForEntity(obj);
                string ns = item.Name.NamespaceName;
                XName idXName = XName.Get(idElementName, ns);
                Func<XElement, bool> equality = x => x.Element(idXName).Value == item.Element(idXName).Value;
                XElement correspondingItem = root.Elements().FirstOrDefault(equality);
                if (correspondingItem == null)
                    root.Add(item);
                else
                    correspondingItem.ReplaceWith(item);
                _changedTables.Add(_provider.GetTableNameForEntity(obj));
            }
        }

        public void Delete(object obj)
        {
            if (obj == null)
                throw new ArgumentNullException();
            XElement item = _converter.ConvertToXElement(obj);
            XElement root = _provider.GetRootForEntity(obj);
            string idElementName = _provider.GetIdElementNameForEntity(obj);
            string ns = item.Name.NamespaceName;
            XName idXName = XName.Get(idElementName, ns);
            Func<XElement, bool> equality = x => x.Element(idXName).Value == item.Element(idXName).Value;
            XElement correspondingItem = root.Elements().FirstOrDefault(equality);
            if (correspondingItem == null)
                throw new ItemNotFoundException(item.Element(idElementName).Value, null);
            else
                correspondingItem.Remove();
            _changedTables.Add(_provider.GetTableNameForEntity(obj));
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                _provider.UpdateTables(_changedTables);
                _disposed = true;
            }
        }
    }
}
